/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ecmascript/runtime/base/builtins_base.h"

#include "plugins/ecmascript/runtime/ecma_string.h"
#include "plugins/ecmascript/runtime/ecma_vm.h"
#include "plugins/ecmascript/runtime/global_env.h"
#include "plugins/ecmascript/runtime/js_array.h"
#include "plugins/ecmascript/runtime/js_array_iterator.h"

#include "plugins/ecmascript/runtime/js_handle.h"
#include "plugins/ecmascript/runtime/js_hclass.h"
#include "plugins/ecmascript/runtime/js_object-inl.h"
#include "plugins/ecmascript/runtime/js_tagged_value-inl.h"
#include "plugins/ecmascript/runtime/js_tagged_value.h"
#include "plugins/ecmascript/runtime/js_thread.h"

#include "plugins/ecmascript/runtime/object_factory.h"
#include "plugins/ecmascript/runtime/object_operator.h"
#include "plugins/ecmascript/tests/runtime/common/test_helper.h"
#include "utils/bit_utils.h"

// NOLINTNEXTLINE(google-build-using-namespace)
using namespace ark::ecmascript;
// NOLINTNEXTLINE(google-build-using-namespace)
using namespace ark::ecmascript::builtins;

// NOLINTBEGIN(readability-magic-numbers)

namespace ark::test {
class BuiltinsArrayTest : public testing::Test {
public:
    static void SetUpTestCase()
    {
        GTEST_LOG_(INFO) << "SetUpTestCase";
    }

    static void TearDownTestCase()
    {
        GTEST_LOG_(INFO) << "TearDownCase";
    }

    void SetUp() override
    {
        TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_);
    }

    void TearDown() override
    {
        TestHelper::DestroyEcmaVMWithScope(instance_, scope_);
    }

    class TestClass {
    public:
        static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv)
        {
            JSHandle<JSTaggedValue> key = builtins_common::GetCallArg(argv, 0);
            if (key->IsUndefined()) {
                return JSTaggedValue::Undefined();
            }
            JSArray *jsArray = JSArray::Cast(builtins_common::GetThis(argv)->GetTaggedObject());
            int length = jsArray->GetArrayLength() + 1;
            jsArray->SetArrayLength(argv->GetThread(), length);
            return JSTaggedValue::Undefined();
        }

        static JSTaggedValue TestEveryFunc(EcmaRuntimeCallInfo *argv)
        {
            uint32_t argc = argv->GetArgsNumber();
            if (argc > 0) {
                if (builtins_common::GetCallArg(argv, 0)->GetInt() > 10) {  // 10 : test case
                    return builtins_common::GetTaggedBoolean(true);
                }
            }
            return builtins_common::GetTaggedBoolean(false);
        }

        static JSTaggedValue TestMapFunc(EcmaRuntimeCallInfo *argv)
        {
            int accumulator = builtins_common::GetCallArg(argv, 0)->GetInt();
            accumulator = accumulator * 2;  // 2 : mapped to 2 times the original value
            return builtins_common::GetTaggedInt(accumulator);
        }

        static JSTaggedValue TestFindFunc(EcmaRuntimeCallInfo *argv)
        {
            uint32_t argc = argv->GetArgsNumber();
            if (argc > 0) {
                // 10 : test case
                if (builtins_common::GetCallArg(argv, 0)->GetInt() > 10) {
                    return builtins_common::GetTaggedBoolean(true);
                }
            }
            return builtins_common::GetTaggedBoolean(false);
        }

        static JSTaggedValue TestFindIndexFunc(EcmaRuntimeCallInfo *argv)
        {
            uint32_t argc = argv->GetArgsNumber();
            if (argc > 0) {
                // 10 : test case
                if (builtins_common::GetCallArg(argv, 0)->GetInt() > 10) {
                    return builtins_common::GetTaggedBoolean(true);
                }
            }
            return builtins_common::GetTaggedBoolean(false);
        }

        static JSTaggedValue TestReduceFunc(EcmaRuntimeCallInfo *argv)
        {
            int accumulator = builtins_common::GetCallArg(argv, 0)->GetInt();
            accumulator = accumulator + builtins_common::GetCallArg(argv, 1)->GetInt();
            return builtins_common::GetTaggedInt(accumulator);
        }

        static JSTaggedValue TestReduceRightFunc(EcmaRuntimeCallInfo *argv)
        {
            int accumulator = builtins_common::GetCallArg(argv, 0)->GetInt();
            accumulator = accumulator + builtins_common::GetCallArg(argv, 1)->GetInt();
            return builtins_common::GetTaggedInt(accumulator);
        }

        static JSTaggedValue TestSomeFunc(EcmaRuntimeCallInfo *argv)
        {
            uint32_t argc = argv->GetArgsNumber();
            if (argc > 0) {
                if (builtins_common::GetCallArg(argv, 0)->GetInt() > 10) {  // 10 : test case
                    return builtins_common::GetTaggedBoolean(true);
                }
            }
            return builtins_common::GetTaggedBoolean(false);
        }

        // x => [x*2]
        static JSTaggedValue TestFlatMapFunc(EcmaRuntimeCallInfo *argv)  //« element, sourceIndex, source »
        {
            auto thread = argv->GetThread();

            // element = [x]
            JSTaggedValue element = builtins_common::GetCallArg(argv, 0).GetTaggedValue();
            [[maybe_unused]] EcmaHandleScope handleScope(thread);
            JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));

            // val = x
            JSTaggedValue val = JSArray::GetProperty(thread, JSHandle<JSTaggedValue>(thread, element), key0)
                                    .GetValue()
                                    .GetTaggedValue();
            int accumulator = val.GetNumber();
            accumulator *= 2;

            return builtins_common::GetTaggedInt(accumulator);
        }
    };

protected:
    // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
    JSThread *thread_ {nullptr};

private:
    PandaVM *instance_ {nullptr};
    EcmaHandleScope *scope_ {nullptr};
};

JSTaggedValue CreateBuiltinsJSObject(JSThread *thread, const PandaString &keyCStr)
{
    EcmaVM *ecmaVm = thread->GetEcmaVM();
    JSHandle<GlobalEnv> globalEnv = ecmaVm->GetGlobalEnv();

    JSHandle<JSTaggedValue> dynclass = globalEnv->GetObjectFunction();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    JSHandle<JSTaggedValue> obj(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(dynclass), dynclass));
    JSHandle<JSTaggedValue> key(factory->NewFromCanBeCompressString(&keyCStr[0]));
    JSHandle<JSTaggedValue> value(thread, JSTaggedValue(1));
    JSObject::SetProperty(thread, obj, key, value);
    return obj.GetTaggedValue();
}

TEST_F(BuiltinsArrayTest, ArrayConstructor)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    JSHandle<JSFunction> array(env->GetArrayFunction());
    JSHandle<JSObject> globalObject(thread_, env->GetGlobalObject());

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10);
    ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(3)));
    ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast<int32_t>(5)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::ArrayConstructor(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());
    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));
}

// 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] )
TEST_F(BuiltinsArrayTest, From)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0))->GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(5)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::From(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());
    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, valueHandle, key3, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4));
    JSObject::GetOwnProperty(thread_, valueHandle, key4, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));
}

// 22.1.2.2 Array.isArray(arg)
TEST_F(BuiltinsArrayTest, IsArray)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetCallArg(0, obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::IsArray(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData());

    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    result = array::IsArray(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

TEST_F(BuiltinsArrayTest, Of)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    JSHandle<JSFunction> array(env->GetArrayFunction());
    JSHandle<JSObject> globalObject(thread_, env->GetGlobalObject());

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10);
    ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(3)));
    ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast<int32_t>(5)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::Of(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());
    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));

    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));
}

TEST_F(BuiltinsArrayTest, Species)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    JSHandle<JSFunction> array(env->GetArrayFunction());
    JSHandle<JSObject> globalObject(thread_, env->GetGlobalObject());

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo1->SetFunction(array.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetThis(globalObject.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::GetSpecies(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_TRUE(result.IsECMAObject());
}

TEST_F(BuiltinsArrayTest, Concat)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    auto *arr1 = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();
    EXPECT_TRUE(arr1 != nullptr);
    JSHandle<JSObject> obj1(thread_, arr1);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(0));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj1, key4, desc4);
    JSHandle<JSTaggedValue> key5(thread_, JSTaggedValue(1));
    PropertyDescriptor desc5(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(5)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj1, key5, desc5);
    JSHandle<JSTaggedValue> key6(thread_, JSTaggedValue(2));
    PropertyDescriptor desc6(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(6)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj1, key6, desc6);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, obj1.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Concat(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());

    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    JSHandle<JSTaggedValue> key7(thread_, JSTaggedValue(5));
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue()->GetInt(),
              6);
    JSObject::GetOwnProperty(thread_, valueHandle, key7, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(6));
}

// 22.1.3.3 new Array(1,2,3,4,5).CopyWithin(0,3,5)
TEST_F(BuiltinsArrayTest, CopyWithin)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(5)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(0)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(3)));
    ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast<int32_t>(5)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::CopyWithin(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());
    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, valueHandle, key3, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4));
    JSObject::GetOwnProperty(thread_, valueHandle, key4, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));
}

TEST_F(BuiltinsArrayTest, Every)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(100)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);

    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(200)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(300)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    JSHandle<JSArray> jsArray = factory->NewJSArray();
    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestEveryFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result2 = array::proto::Every(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData());
}

TEST_F(BuiltinsArrayTest, Map)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(50)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(200)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSArray> jsArray(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)));
    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestMapFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Map(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());

    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue()->GetInt(),
              3);
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);

    ASSERT_EQ(descRes.GetValue()->GetInt(), 100);
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue()->GetInt(), 400);
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue()->GetInt(), 6);
}

TEST_F(BuiltinsArrayTest, Reverse)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(50)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(200)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Reverse(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());

    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue()->GetInt(),
              3);
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(200));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(50));
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 3);
    JSObject::GetOwnProperty(thread_, obj, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, obj, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(200));
    JSObject::GetOwnProperty(thread_, obj, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(50));
}

TEST_F(BuiltinsArrayTest, Slice)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(4)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Slice(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());

    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue()->GetInt(),
              3);
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4));
}

TEST_F(BuiltinsArrayTest, Splice)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(5)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(2)));
    ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast<int32_t>(100)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Splice(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 4);

    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue()->GetInt(),
              2);
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2));
}

// ES2019 22.1.3.10 Builtin Array.flat()
TEST_F(BuiltinsArrayTest, Flat)
{
    ASSERT_NE(thread_, nullptr);
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();

    auto *arr1 = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();
    auto *arr2 = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();
    auto *arr3 = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();

    EXPECT_TRUE(arr1 != nullptr);
    EXPECT_TRUE(arr2 != nullptr);
    EXPECT_TRUE(arr3 != nullptr);

    JSHandle<JSObject> obj1(thread_, arr1);
    JSHandle<JSObject> obj2(thread_, arr2);
    JSHandle<JSObject> obj3(thread_, arr3);

    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj1), lengthKeyHandle).GetValue()->GetInt(), 0);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj2), lengthKeyHandle).GetValue()->GetInt(), 0);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj3), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));

    // arr1 = [0, 1, arr2]
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0)), true, true, true);
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    PropertyDescriptor descNested1(thread_, JSHandle<JSTaggedValue>(thread_, obj2.GetTaggedValue()), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj1, key0, desc0);
    JSArray::DefineOwnProperty(thread_, obj1, key1, desc1);
    JSArray::DefineOwnProperty(thread_, obj1, key2, descNested1);

    // arr2 = [2, 3, arr3]
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    PropertyDescriptor descNested2(thread_, JSHandle<JSTaggedValue>(thread_, obj3.GetTaggedValue()), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj2, key0, desc2);
    JSArray::DefineOwnProperty(thread_, obj2, key1, desc3);
    JSArray::DefineOwnProperty(thread_, obj2, key2, descNested2);

    // arr3 = [4, 5]
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    PropertyDescriptor desc5(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(5)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj3, key0, desc4);
    JSArray::DefineOwnProperty(thread_, obj3, key1, desc5);

    // [0, 1, [2, 3, [4, 5]]].flat(2) = [0, 1, 2, 3, 4, 5]
    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj1.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(2)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Flat(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    PropertyDescriptor descRes(thread_);

    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    JSHandle<JSObject> valueHandle(thread_, value);

    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue()->GetInt(),
              6);

    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    JSHandle<JSTaggedValue> key5(thread_, JSTaggedValue(5));

    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2));
    JSObject::GetOwnProperty(thread_, valueHandle, key3, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));
    JSObject::GetOwnProperty(thread_, valueHandle, key4, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4));
    JSObject::GetOwnProperty(thread_, valueHandle, key5, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));

    // [0, 1, [2, 3, [4, 5]]].flat() = [0, 1, 2, 3, [4, 5]]
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(obj1.GetTaggedValue());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    result = array::proto::Flat(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSTaggedValue value2(static_cast<JSTaggedType>(result.GetRawData()));
    JSHandle<JSObject> valueHandle2(thread_, value2);

    EXPECT_EQ(
        JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle2), lengthKeyHandle).GetValue()->GetInt(), 5);

    JSObject::GetOwnProperty(thread_, valueHandle2, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0));
    JSObject::GetOwnProperty(thread_, valueHandle2, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1));
    JSObject::GetOwnProperty(thread_, valueHandle2, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(2));
    JSObject::GetOwnProperty(thread_, valueHandle2, key3, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(3));

    JSObject::GetOwnProperty(thread_, valueHandle2, key4, descRes);
    ASSERT_TRUE(descRes.GetValue().GetTaggedValue().IsECMAObject());

    JSTaggedValue innerArrValue(descRes.GetValue().GetTaggedValue());
    JSHandle<JSObject> innerArrHandle(thread_, innerArrValue);

    EXPECT_EQ(
        JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(innerArrHandle), lengthKeyHandle).GetValue()->GetInt(),
        2);

    JSObject::GetOwnProperty(thread_, innerArrHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4));
    JSObject::GetOwnProperty(thread_, innerArrHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));
}

// ES2019 22.1.3.11 Builtin Array.flatMap()
TEST_F(BuiltinsArrayTest, FlatMap)
{
    // [[1], [2], [4]].flatMap(x => [x*2]) = [2, 4, 8]
    ASSERT_NE(thread_, nullptr);
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    auto *arr = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();
    auto *arr1 = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();
    auto *arr2 = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();
    auto *arr3 = JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetObject<JSArray>();

    EXPECT_TRUE(arr != nullptr);
    EXPECT_TRUE(arr1 != nullptr);
    EXPECT_TRUE(arr2 != nullptr);
    EXPECT_TRUE(arr3 != nullptr);

    JSHandle<JSObject> obj(thread_, arr);
    JSHandle<JSObject> obj1(thread_, arr1);
    JSHandle<JSObject> obj2(thread_, arr2);
    JSHandle<JSObject> obj3(thread_, arr3);

    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj1), lengthKeyHandle).GetValue()->GetInt(), 0);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj2), lengthKeyHandle).GetValue()->GetInt(), 0);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj3), lengthKeyHandle).GetValue()->GetInt(), 0);

    // create [1], [2], [4] elements
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedNumber(0));
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedNumber(1));
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedNumber(2));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSObject::DefineOwnProperty(thread_, obj1, key0, desc1);
    JSObject::DefineOwnProperty(thread_, obj2, key0, desc2);
    JSObject::DefineOwnProperty(thread_, obj3, key0, desc4);

    // arr = [[1], [2], [4]]
    PropertyDescriptor objDesc1(thread_, JSHandle<JSTaggedValue>(thread_, obj1.GetTaggedValue()), true, true, true);
    PropertyDescriptor objDesc2(thread_, JSHandle<JSTaggedValue>(thread_, obj2.GetTaggedValue()), true, true, true);
    PropertyDescriptor objDesc4(thread_, JSHandle<JSTaggedValue>(thread_, obj3.GetTaggedValue()), true, true, true);
    JSObject::DefineOwnProperty(thread_, obj, key0, objDesc1);
    JSObject::DefineOwnProperty(thread_, obj, key1, objDesc2);
    JSObject::DefineOwnProperty(thread_, obj, key2, objDesc4);

    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj1), lengthKeyHandle).GetValue()->GetInt(), 1);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj2), lengthKeyHandle).GetValue()->GetInt(), 1);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj3), lengthKeyHandle).GetValue()->GetInt(), 1);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 3);

    JSHandle<JSFunction> cb = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestFlatMapFunc));
    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, cb.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::FlatMap(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    PropertyDescriptor resDesc(thread_);

    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    JSHandle<JSObject> valueHandle(thread_, value);

    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue()->GetInt(),
              3);

    JSObject::GetOwnProperty(thread_, valueHandle, key0, resDesc);
    ASSERT_EQ(resDesc.GetValue().GetTaggedValue(), JSTaggedValue(2));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, resDesc);
    ASSERT_EQ(resDesc.GetValue().GetTaggedValue(), JSTaggedValue(4));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, resDesc);
    ASSERT_EQ(resDesc.GetValue().GetTaggedValue(), JSTaggedValue(8));
}

// 22.1.3.6 new Array(1,2,3,4,5).Fill(0,1,3)
TEST_F(BuiltinsArrayTest, Fill)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(5)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 10);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(0)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(1)));
    ecmaRuntimeCallInfo1->SetCallArg(2, JSTaggedValue(static_cast<int32_t>(3)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Fill(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());
    PropertyDescriptor descRes(thread_);
    JSHandle<JSObject> valueHandle(thread_, value);
    JSObject::GetOwnProperty(thread_, valueHandle, key0, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(1));
    JSObject::GetOwnProperty(thread_, valueHandle, key1, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0));
    JSObject::GetOwnProperty(thread_, valueHandle, key2, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(0));
    JSObject::GetOwnProperty(thread_, valueHandle, key3, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(4));
    JSObject::GetOwnProperty(thread_, valueHandle, key4, descRes);
    ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(5));
}

TEST_F(BuiltinsArrayTest, Find)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(102)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSArray> jsArray(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)));
    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestFindFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result2 = array::proto::Find(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_EQ(result2.GetRawData(), JSTaggedValue(102).GetRawData());
}

TEST_F(BuiltinsArrayTest, FindIndex)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);

    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(30)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    JSHandle<JSArray> jsArray(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)));
    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestFindIndexFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result2 = array::proto::FindIndex(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_EQ(result2.GetRawData(), JSTaggedValue(static_cast<double>(2)).GetRawData());
}

TEST_F(BuiltinsArrayTest, ForEach)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    JSHandle<JSArray> jsArray(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)));
    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestForEachFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result2 = array::proto::ForEach(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    EXPECT_EQ(result2.GetRawData(), JSTaggedValue::VALUE_UNDEFINED);
    EXPECT_EQ(jsArray->GetArrayLength(), 3);
}

// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ])
TEST_F(BuiltinsArrayTest, Includes1)
{
    ASSERT_NE(thread_, nullptr);

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    // new Array(1,2,3,4,3).includes(1,0)
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));
    ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(0)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = array::proto::Includes(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(true).GetRawData());
}

// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ])
TEST_F(BuiltinsArrayTest, Includes2)
{
    ASSERT_NE(thread_, nullptr);

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    // new Array(1,2,3,4,3).includes(1,3)
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));
    ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(3)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = array::proto::Includes(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData());
}

// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ])
TEST_F(BuiltinsArrayTest, Includes3)
{
    ASSERT_NE(thread_, nullptr);

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    // new Array(1,2,3,4,3).includes(5,0)
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(5)));
    ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(0)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = array::proto::Includes(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData());
}

// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ])
TEST_F(BuiltinsArrayTest, Includes4)
{
    ASSERT_NE(thread_, nullptr);

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    // new Array(1,2,3,4,3).includes(1)
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(1)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = array::proto::Includes(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(true).GetRawData());
}

// ES2021 23.1.3.13 new Array(1,2,3,4,3).includes(searchElement [ , fromIndex ])
TEST_F(BuiltinsArrayTest, Includes5)
{
    ASSERT_NE(thread_, nullptr);

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetHeapObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    // new Array(1,2,3,4,3).includes(5)
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(5)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = array::proto::Includes(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(false).GetRawData());
}

// 22.1.3.11 new Array(1,2,3,4,3).IndexOf(searchElement [ , fromIndex ])
TEST_F(BuiltinsArrayTest, IndexOf)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(3)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(0)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::IndexOf(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(2)).GetRawData());

    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(3)));
    ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(3)));

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    result = array::proto::IndexOf(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(4)).GetRawData());

    auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(5)));
    ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(0)));

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo3.get());
    result = array::proto::IndexOf(ecmaRuntimeCallInfo3.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData());

    auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(3)));

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo4.get());
    result = array::proto::IndexOf(ecmaRuntimeCallInfo4.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(2)).GetRawData());
}

// 22.1.3.14 new Array(1,2,3,4,3).LastIndexOf(searchElement [ , fromIndex ])
TEST_F(BuiltinsArrayTest, LastIndexOf)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    PropertyDescriptor desc3(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key3, desc3);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    PropertyDescriptor desc4(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key4, desc4);

    // new Array(1,2,3,4,3).LastIndexOf(3,4)
    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(3)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(4)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::LastIndexOf(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(4)).GetRawData());

    // new Array(1,2,3,4,3).LastIndexOf(3,3)
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(3)));
    ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(3)));

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    result = array::proto::LastIndexOf(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(2)).GetRawData());

    // new Array(1,2,3,4,3).LastIndexOf(5,4)
    auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(5)));
    ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(4)));

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo3.get());
    result = array::proto::LastIndexOf(ecmaRuntimeCallInfo3.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData());

    // new Array(1,2,3,4,3).LastIndexOf(3)
    auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(3)));

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo4.get());
    result = array::proto::LastIndexOf(ecmaRuntimeCallInfo4.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(4)).GetRawData());
}

// 22.1.3.11 new Array().Pop()
TEST_F(BuiltinsArrayTest, Pop)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Pop(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    result = array::proto::Pop(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData());
}

// 22.1.3.11 new Array(1,2,3).Push(...items)
TEST_F(BuiltinsArrayTest, Push)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(4)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(5)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Push(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetNumber(), 5);

    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 5);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(3));
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), key3).GetValue()->GetInt(), 4);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(4));
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), key4).GetValue()->GetInt(), 5);
}

TEST_F(BuiltinsArrayTest, Reduce)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestReduceFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(10)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Reduce(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(16).GetRawData());
}

TEST_F(BuiltinsArrayTest, ReduceRight)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);
    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestReduceRightFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(10)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::ReduceRight(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(16).GetRawData());
}

TEST_F(BuiltinsArrayTest, Shift)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Shift(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(1).GetRawData());
}

TEST_F(BuiltinsArrayTest, Some)
{
    auto ecmaVm = thread_->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
    ObjectFactory *factory = ecmaVm->GetFactory();

    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(20)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    JSHandle<JSArray> jsArray(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)));
    JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestSomeFunc));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result2 = array::proto::Some(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    ASSERT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData());
}

TEST_F(BuiltinsArrayTest, Sort)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result2 = array::proto::Sort(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result2.IsECMAObject());
    JSHandle<JSTaggedValue> resultArr =
        JSHandle<JSTaggedValue>(thread_, JSTaggedValue(static_cast<JSTaggedType>(result2.GetRawData())));
    EXPECT_EQ(JSArray::GetProperty(thread_, resultArr, key0).GetValue()->GetInt(), 1);
    EXPECT_EQ(JSArray::GetProperty(thread_, resultArr, key1).GetValue()->GetInt(), 2);
    EXPECT_EQ(JSArray::GetProperty(thread_, resultArr, key2).GetValue()->GetInt(), 3);
}

TEST_F(BuiltinsArrayTest, Unshift)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSObject> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(1)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)), true, true, true);
    JSArray::DefineOwnProperty(thread_, obj, key2, desc2);

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());
    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(4)));
    ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast<int32_t>(5)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Unshift(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(5)).GetRawData());

    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), lengthKeyHandle).GetValue()->GetInt(), 5);
    JSHandle<JSTaggedValue> key3(thread_, JSTaggedValue(0));
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), key3).GetValue()->GetInt(), 4);
    JSHandle<JSTaggedValue> key4(thread_, JSTaggedValue(1));
    EXPECT_EQ(JSArray::GetProperty(thread_, JSHandle<JSTaggedValue>(obj), key4).GetValue()->GetInt(), 5);
}

TEST_F(BuiltinsArrayTest, Join)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSTaggedValue> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, obj, lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)));
    JSArray::DefineOwnProperty(thread_, JSHandle<JSObject>(obj), key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)));
    JSArray::DefineOwnProperty(thread_, JSHandle<JSObject>(obj), key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)));
    JSArray::DefineOwnProperty(thread_, JSHandle<JSObject>(obj), key2, desc2);

    JSHandle<EcmaString> str = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2,3,4");
    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Join(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSHandle<EcmaString> resultHandle(thread_, reinterpret_cast<EcmaString *>(result.GetRawData()));
    [[maybe_unused]] auto *res = EcmaString::Cast(resultHandle.GetTaggedValue().GetTaggedObject());

    ASSERT_EQ(res->Compare(*str), 0);
}

TEST_F(BuiltinsArrayTest, ToString)
{
    JSHandle<JSTaggedValue> lengthKeyHandle = thread_->GlobalConstants()->GetHandledLengthString();
    JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread_, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
    EXPECT_TRUE(arr != nullptr);
    JSHandle<JSTaggedValue> obj(thread_, arr);
    EXPECT_EQ(JSArray::GetProperty(thread_, obj, lengthKeyHandle).GetValue()->GetInt(), 0);

    JSHandle<JSTaggedValue> key0(thread_, JSTaggedValue(0));
    PropertyDescriptor desc0(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(2)));
    JSArray::DefineOwnProperty(thread_, JSHandle<JSObject>(obj), key0, desc0);
    JSHandle<JSTaggedValue> key1(thread_, JSTaggedValue(1));
    PropertyDescriptor desc1(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(3)));
    JSArray::DefineOwnProperty(thread_, JSHandle<JSObject>(obj), key1, desc1);
    JSHandle<JSTaggedValue> key2(thread_, JSTaggedValue(2));
    PropertyDescriptor desc2(thread_, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(4)));
    JSArray::DefineOwnProperty(thread_, JSHandle<JSObject>(obj), key2, desc2);

    JSHandle<EcmaString> str = thread_->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("2,3,4");
    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result = array::proto::Join(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);
    JSHandle<EcmaString> resultHandle(thread_, reinterpret_cast<EcmaString *>(result.GetRawData()));
    [[maybe_unused]] auto *res = EcmaString::Cast(resultHandle.GetTaggedValue().GetTaggedObject());

    ASSERT_EQ(res->Compare(*str), 0);
}
}  // namespace ark::test

// NOLINTEND(readability-magic-numbers)
